package function

import (
	"fmt"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	. "go_tempate/common/store"
	. "go_tempate/config"
	"os"
	"time"
)

// 定义一个全局 logger 实例
// Logger提供快速、分级、结构化的日志记录。所有方法对于并发使用都是安全的。
// Logger是为每一微秒和每一个分配都很重要的上下文设计的，
// 因此它的API有意倾向于性能和类型安全，而不是简便性。
// 对于大多数应用程序，SugaredLogger在性能和人体工程学之间取得了更好的平衡。
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
var Logger *zap.Logger

// SugaredLogger将基本的Logger功能封装在一个较慢但不那么冗长的API中。任何Logger都可以通过其Sugar方法转换为sugardlogger。
//与Logger不同，SugaredLogger并不坚持结构化日志记录。对于每个日志级别，它公开了四个方法:
//   - methods named after the log level for log.Print-style logging
//   - methods ending in "w" for loosely-typed structured logging
//   - methods ending in "f" for log.Printf-style logging
//   - methods ending in "ln" for log.Println-style logging

// For example, the methods for InfoLevel are:
//
//	Info(...any)           Print-style logging
//	Infow(...any)          Structured logging (read as "info with")
//	Infof(string, ...any)  Printf-style logging
//	Infoln(...any)         Println-style logging
//
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
var SugaredLogger *zap.SugaredLogger

// 初始化日志函数
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func InitLogger() error {
	// 判断文件夹是否存在
	// ----------------------------------------------------------------------------------------------------------
	_, err := os.Stat(LogFolder)

	if os.IsNotExist(err) {
		// 文件夹不存在，创建文件夹
		err := os.Mkdir(LogFolder, 0755)
		if err != nil {
			fmt.Println(FolderCreationFailed, err)
			return err
		}
	} else if err != nil {
		// 其他错误
		fmt.Println(FailedToCheckFolder, err)
		return err
	}

	// 定制日志的格式
	// ----------------------------------------------------------------------------------------------------------
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

	// Logger, _ := zap.NewProduction()  // 用于项目生产阶段，格式: json【适合用于集成到第三方日志分析系统中的】
	// Logger, _ := zap.NewDevelopment()    // 用于项目开发阶段，格式: 普通文本格式【适合在终端查看】
	Logger = zap.New(core, zap.AddCaller())

	// Sugar封装了Logger，以提供更符合人体工程学的API，但速度略慢。糖化一个Logger的成本非常低，
	// 因此一个应用程序同时使用Loggers和SugaredLoggers是合理的，在性能敏感代码的边界上在它们之间进行转换。
	SugaredLogger = Logger.Sugar()

	// 替换了zap的全局日志配置
	zap.ReplaceGlobals(Logger)

	return nil
}

// 日志格式化
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func getEncoder() zapcore.Encoder {
	// 使用zap提供的 NewProductionEncoderConfig
	encoderConfig := zap.NewProductionEncoderConfig()
	// 设置时间格式
	encoderConfig.EncodeTime = CustomTimeEncoder
	// 时间的key
	encoderConfig.TimeKey = "time"
	// 级别
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
	// 显示调用者信息
	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder

	//判断是根据json日志输出
	if Format == "json" {
		return zapcore.NewJSONEncoder(encoderConfig)
	}

	//默认为 console
	return zapcore.NewConsoleEncoder(encoderConfig)
}

// zap日志修改展示格式
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func getLogWriter() zapcore.WriteSyncer {
	// Logger is an io.WriteCloser that writes to the specified filename.
	// 日志记录器在第一次写入时打开或创建日志文件。如果文件存在并且小于MaxSize兆字节，则lumberjack将打开并追加该文件。
	// 如果该文件存在并且其大小为>= MaxSize兆字节，
	// 则通过将当前时间放在文件扩展名(或者如果没有扩展名则放在文件名的末尾)的名称中的时间戳中来重命名该文件。
	// 然后使用原始文件名创建一个新的日志文件。
	// 每当写操作导致当前日志文件超过MaxSize兆字节时，将关闭当前文件，重新命名，并使用原始名称创建新的日志文件。
	// 因此，您给Logger的文件名始终是“当前”日志文件。
	// 如果MaxBackups和MaxAge均为0，则不会删除旧的日志文件。
	lumberJackLogger := &lumberjack.Logger{
		// Filename是要写入日志的文件。备份日志文件将保留在同一目录下
		Filename: Filename,

		// MaxSize是日志文件旋转之前的最大大小(以兆字节为单位)。默认为100兆字节。
		MaxSize: MaxSize, // 单位M

		// MaxBackups是要保留的旧日志文件的最大数量。默认是保留所有旧的日志文件(尽管MaxAge仍然可能导致它们被删除)。
		MaxBackups: MaxBackups, // 备份数量

		// MaxAge是根据文件名中编码的时间戳保留旧日志文件的最大天数。
		// 请注意，一天被定义为24小时，由于夏令时、闰秒等原因，可能与日历日不完全对应。默认情况下，不根据时间删除旧的日志文件。
		MaxAge: MaxAge, // 备份天数

		// Compress决定是否应该使用gzip压缩旋转的日志文件。默认情况下不执行压缩。
		Compress: Compress, // 是否压缩
	}
	return zapcore.AddSync(lumberJackLogger)
}

// CustomTimeEncoder 自定义日志输出时间格式
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}
